package satir;

import java.util.*;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import satir.card.*;
import satir.card.factory.*;
import satir.cell.*;
import satir.command.*;
import satir.framework.*;
import satir.framework.JSONIOPerformer.*;
import satir.player.*;

/**
 * The Class StairGame.
 * 
 * @version 2.0 March 2012
 * @author Bill Chan
 */
public class SatirGame {
	
	public static SatirGame constant;

	/** The board. */
	private Board board;

	/** The num of player. */
	private int numOfPlayer;

	/** The current player index. */
	private int currentPlayerIndex;

	/** The players. */
	private ArrayList<Player> players;

	/** The card factory. */
	private ArrayList<CardFactory> cardFactorys;

	/** The role card. */
	private ArrayList<RoleCard> roleCards;

	/** The magic tool card. */
	private ArrayList<MagicToolCard> magicToolCards;
	
	/** The configure detail of command. */
	private JSONObject cmdConfig;

	/**
	 * Instantiates a new stair game.
	 * It will read the Command.config for command creation
	 */
	public SatirGame() {
		players = new ArrayList<Player>();
		cardFactorys = new ArrayList<CardFactory>();
		roleCards = new ArrayList<RoleCard>();
		magicToolCards = new ArrayList<MagicToolCard>();
		board = new Board();
		JSONIOPerformer cmdConfigIO;
		try {
			cmdConfigIO = new JSONIOPerformer("Command.config");
			cmdConfig = cmdConfigIO.retrieve();
		} catch (JSONIOPerformerReadException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (JSONIOPerformerJSONDecodeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		constant = this;
	}

	/**
	 * Start the game.
	 * Control the flow of board game
	 *
	 */
	public void startGame() {
		// initialize
		initFactory();
		// Begin to receive all initial input from players
		initInput();
		// Game begin
		currentPlayerIndex = 0;
		Scanner scanner = new Scanner(System.in);
		JSONObject userCmdMapping;
		try {
			//Get the command mapping from cmdConfig
			userCmdMapping = cmdConfig.getJSONObject("userCmdMapping");

			// main flow
			while (true) {
				String inputCommandString;
				boolean isSameTurn = true;
				do {
					if (players.get(currentPlayerIndex).getSelfEsteem() == 0) {
						System.out.println("player [" + players.get(currentPlayerIndex).getName() + "] your selfEsteem is zero, stop a round!!");
						players.get(currentPlayerIndex).setSelfEsteem(1);
						break;
					}

					// Let player to select the action,such as RoolDice,View your status
					System.out.println("player [" + players.get(currentPlayerIndex).getName() + "] now is your turn!!!Input:");
					System.out.println("[1] roll a dice");
					System.out.println("[2] view status");
					System.out.println("[3] show position");
					inputCommandString = scanner.next();

					int inputCmd = Integer.parseInt(inputCommandString);
					//Use LazyInitiator to create an command instance for execution
					Command cmd = LazyInitiator.getCommand(userCmdMapping.getString(inputCommandString), true);
					cmd.execute();

					// break the loop and when roll dice (move to next player)
					isSameTurn = (inputCmd != 1);
				} while (isSameTurn);

				if (players.get(currentPlayerIndex).getSelfEsteem() == 10) {
					System.out.println(players.get(currentPlayerIndex).getName() + " is winner!");
					System.exit(0);
				}
				currentPlayerIndex = (currentPlayerIndex + 1) % numOfPlayer;
			}
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Inits the factory.
	 * After the problem of role card is solved,
	 * This method will be removed
	 */
	private void initFactory() {
		try {
			String cardFactoryString[] = { "RoleCardFactory" };
			// create card factory
			for (int i = 0; i < cardFactoryString.length; i++) {
				cardFactorys.add((CardFactory) Class.forName("satir.card.factory." + cardFactoryString[i]).newInstance());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Initialization of game by user input.
	 */
	private void initInput() {
		try {
			//Get the execution order of command from cmdConfig
			JSONArray initCmdOrder = cmdConfig.getJSONArray("initCmdOrder");
			for (int i = 0; i < initCmdOrder.length(); i++) {
				//Use LazyInitiator to create an instance of command for execution
				Command cmd = LazyInitiator.getCommand(initCmdOrder.getString(i), true);
				cmd.execute();
				// create role card only after inputting number of player
				//TODO should be removed
				if (i == 0) {
					for (int j = 0; j < numOfPlayer; j++) {
						roleCards.add((RoleCard) cardFactorys.get(0).createCard());
					}
				}
			}
		} catch (JSONException e) {
			e.printStackTrace();
		}
		// place the player to board
		for (int i = 0; i < numOfPlayer; i++) {
			players.get(i).setCurrentCell(board.getHead());
			board.getHead().addPlayer(players.get(i));
		}
	}

	/**
	 * Show players list.
	 */
	public void showPlayersList() {
		System.out.println("Here is the players list:");

		for (int i = 0; i < numOfPlayer; i++) {
			String playerOrder = "1st";
			if (i == 1)
				playerOrder = "2nd";
			else if (i == 2)
				playerOrder = "3rd";
			else if (i > 2)
				playerOrder = (i + 1) + "th";

			System.out.println(playerOrder + " player [" + players.get(i).getName() + "] figure [" + players.get(i).getFigure() + "] role :" + players.get(i).getRole().getName());
		}
		System.out.println("\n");
	}

	/**
	 * Sets the num of player.
	 * 
	 * @param numOfPlayer
	 *            the new num of player
	 */
	public void setNumOfPlayer(int numOfPlayer) {
		this.numOfPlayer = numOfPlayer;
	}

	/**
	 * Gets the num of player.
	 * 
	 * @return the num of player
	 */
	public int getNumOfPlayer() {
		return numOfPlayer;
	}

	/**
	 * Gets the current player.
	 * 
	 * @return the current player
	 */
	public Player getCurrentPlayer() {
		return players.get(currentPlayerIndex);
	}

	/**
	 * Gets the board.
	 * 
	 * @return the board
	 */
	public Board getBoard() {
		return board;
	}

	/**
	 * End game.
	 */
	public void endGame() {
		throw new UnsupportedOperationException();
	}

	/**
	 * Gets the players.
	 * 
	 * @param i
	 *            the i
	 * @return the players
	 */
	public Player getPlayers(int i) {
		return players.get(i);
	}

	/**
	 * Gets the array list player.
	 * 
	 * @return the array list player
	 */
	public ArrayList<Player> getArrayListPlayer() {
		return players;
	}

	/**
	 * Sets the array list player.
	 * 
	 * @param players
	 *            the new array list player
	 */
	public void setArrayListPlayer(ArrayList<Player> players) {
		this.players = players;
	}

	/**
	 * Gets the array list role card.
	 * 
	 * @return the array list role card
	 */
	public ArrayList<RoleCard> getArrayListRoleCard() {
		return roleCards;
	}

	/**
	 * Gets the array list magic tool card.
	 * 
	 * @return the array list magic tool card
	 */
	public ArrayList<MagicToolCard> getArrayListMagicToolCard() {
		return magicToolCards;
	}

	/**
	 * Checks if is win.
	 * 
	 * @param head
	 *            the head
	 * @param current
	 *            the current
	 * @return true, if is win
	 */
	public boolean isWin(Cell head, Cell current) {
		return head.equals(current);
	}
}